package core

import (
	"fmt"
	"path"
	"path/filepath"
	"reflect"
	"strings"

	"gitee.com/minph/fileScript/common"
	"github.com/minph/fs"
	"github.com/minph/fs/dir"
	"github.com/minph/fs/utils"
)

// Node 解析节点结构体
type Node struct {
	Cxt         interface{}
	Caller      string
	Callee      string
	ContextNode *ContextNode
	Line        int
}

// NewNode 创建解析节点结构体
func NewNode() *Node {
	return &Node{}
}

// Parse 解析 @ 语句
func Parse(code Code) *Node {
	res := NewNode()

	if AnnoTagsDefault.Include(string(code)) {
		res.Caller = "@Main"
		return res
	}

	i1 := strings.Index(string(code), "(")
	i2 := strings.Index(string(code), ")")

	if i1 == -1 || i2 == -1 {
		return res
	}

	res.Caller = string(code)[:i1]
	res.Callee = string(code)[i1+1 : i2]

	return res
}

// Run 执行
func (n *Node) Run() {

	if n == nil {
		return
	}

	if AnnoTagsDefault.Include(n.Caller) {
		fmt.Println("FileScript start running ...")
		return
	}
	if n.MatchCaller("Print") || n.MatchCaller("Println") {

		o := make(map[string]interface{})
		o["@Print"] = fmt.Print
		o["@Println"] = fmt.Println

		val := GetCtx(n.Callee, n.ContextNode)
		if run, ok := o[n.Caller].(func(x ...interface{}) (n int, err error)); ok {
			run(val)
		}
		return
	}

	if n.MatchCaller("Bind") {
		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@Bind]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@Bind 语句格式必须为(绑定的变量, 常量)"))
		}

		n.ContextNode.Vars[vars[0]] = GetCtx(vars[1], n.ContextNode)
		n.ContextNode.prev.Vars[vars[0]] = GetCtx(vars[1], n.ContextNode)
		return
	}

	if n.MatchCaller("BindScope") {
		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@Bind]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@Bind 语句格式必须为(绑定的变量, 常量)"))
		}

		n.ContextNode.Vars[vars[0]] = GetCtx(vars[1], n.ContextNode)
		return
	}

	if n.MatchCaller("BindRoot") {
		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@BindRoot]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@BindRoot 语句格式必须为(绑定的变量, 常量)"))
		}

		env := n.ContextNode.prev
		for {
			env.Vars[vars[0]] = GetCtx(vars[1], n.ContextNode)
			if env.prev == nil {
				break
			}

			env = env.prev
		}
		return
	}

	if n.MatchCaller("File") {
		p := ""
		if common.IsStaticStr(n.Callee) {
			p = common.ToStaticStr(n.Callee)
		}

		n.ContextNode.Vars["Path"] = p
		n.ContextNode.Vars["Dir"] = path.Dir(p)
		n.ContextNode.Vars["Ext"] = path.Ext(p)
		n.ContextNode.Vars["Name"] = path.Base(p)
		n.ContextNode.Vars["AbsPath"], _ = filepath.Abs(p)

		content, err := fs.ReadAll(p)

		if err != nil {
			errorType := "Error@FileScript/core.Parse[PATH_NOT_FOUND@File]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("路径[%v]读取失败", p))
		}
		n.ContextNode.Vars["Content"] = content

		lines, err := fs.ReadLines(p)

		if err != nil {
			errorType := "Error@FileScript/core.Parse[PATH_NOT_FOUND@File]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("路径[%v]读取失败", p))
		}

		n.ContextNode.Vars["Lines"] = len(lines)
		return
	}

	if n.MatchCaller("Delete") {

		switch path := GetCtx(n.Callee, n.ContextNode).(type) {
		case string:
			{
				if !utils.Exist(path) {
					errorType := "Error@FileScript/core.Parse[PATH_NOT_FOUND@Delete]"
					common.PrintErrorAndExit(errorType, fmt.Sprintf("路径[%v]读取失败", path))
				}

				fs.RemoveAll(path)
				fmt.Printf("删除路径[%v]完成\n", path)
			}
		}

		return
	}

	if n.MatchCaller("Clear") {

		switch path := GetCtx(n.Callee, n.ContextNode).(type) {
		case string:
			{
				if !utils.Exist(path) {
					errorType := "Error@FileScript/core.Parse[PATH_NOT_FOUND@Clear]"
					common.PrintErrorAndExit(errorType, fmt.Sprintf("路径[%v]读取失败", path))
				}

				err := fs.Clear(path)
				if err != nil {
					errorType := "Error@FileScript/core.Parse[RUNNING_ERR@Clear]"
					common.PrintErrorAndExit(errorType, fmt.Sprintf("运行时错误:\n%v", err))
				}
				fmt.Printf("清除文件内容[%v]完成\n", path)
			}
		}

		return
	}

	if n.MatchCaller("Create") {

		if path, ok := GetCtx(n.Callee, n.ContextNode).(string); ok {

			if utils.Exist(path) {
				errorType := "Warn@FileScript/core.Parse[PATH_HAS_CREATED@Create]"
				common.PrintError(errorType, fmt.Sprintf("路径[%v]已存在，无法执行创建", path))
				return
			}

			fs.Create(path)
			fmt.Printf("创建路径[%v]完成\n", path)
			return

		}

		errorType := "Error@FileScript/core.Parse[BAD_PARAMS@Create]"
		common.PrintErrorAndExit(errorType, fmt.Sprintf("标签 @Create 输入了无效的参数[%v]", GetCtx(n.Callee, n.ContextNode)))
		return
	}

	if n.MatchCaller("Debug") {

		tag := ""

		if debugTag, ok := GetCtx(n.Callee, n.ContextNode).(string); ok {
			tag = "#" + debugTag + " "
		}
		fmt.Printf("%v标签[%v]下的环境变量\n", tag, n.Caller)

		for key, val := range n.ContextNode.prev.Vars {

			valType := reflect.TypeOf(val)
			if valType.Kind() == reflect.String {
				fmt.Printf("$.%v = \"%v\"\n", key, val)
				continue
			}

			if valType.Kind() == reflect.Int {
				fmt.Printf("$.%v = int(%v)\n", key, val)
				continue
			}

			if valType.Kind() == reflect.Float64 {
				fmt.Printf("$.%v = float(%v)\n", key, val)
				continue
			}

			if valType.Kind() == reflect.Bool {
				fmt.Printf("$.%v = bool(%v)\n", key, val)
				continue
			}

			fmt.Printf("$.%v = %v\n", key, val)
		}
		fmt.Println("-----------------------")
		return
	}

	if n.MatchCaller("DebugMain") {

		head := n.ContextNode

		for {
			head = head.prev
			if head.prev == nil {
				break
			}
		}

		children := seekNodes(head)

		for _, child := range children {
			fmt.Printf("语句 %v 下环境变量\n", child.Code)
			if child.prev != nil {
				fmt.Printf("父级语句为 %v \n", child.prev.Code)
			} else {
				fmt.Println("<FileScript 根语句>")
			}
			for key, val := range child.Vars {
				valType := reflect.TypeOf(val)
				if valType.Kind() == reflect.String {
					fmt.Printf("$.%v = \"%v\"\n", key, val)
					continue
				}

				if valType.Kind() == reflect.Int {
					fmt.Printf("$.%v = int(%v)\n", key, val)
					continue
				}

				if valType.Kind() == reflect.Float64 {
					fmt.Printf("$.%v = float(%v)\n", key, val)
					continue
				}

				if valType.Kind() == reflect.Bool {
					fmt.Printf("$.%v = bool(%v)\n", key, val)
					continue
				}

				fmt.Printf("$.%v = %v\n", key, val)
			}

			fmt.Println("-----------------------")

		}
		return
	}

	if n.MatchCaller("If") {
		flag := false
		val := GetCtx(n.Callee, n.ContextNode)

		if v, ok := val.(string); ok {
			flag = !(len(v) == 0)
		} else {
			flag = !(val == false || val == 0)
		}

		if flag {
			n.RunChildren(nil)
		} else {
			n.SkipNode()
		}

		return
	}

	if n.MatchCaller("Ingore") {
		n.SkipNode()
		return
	}

	if n.MatchCaller("For") {
		val := GetCtx(n.Callee, n.ContextNode)

		if num, ok := val.(int); ok {

			for i := 0; i < num; i++ {
				n.ContextNode.Vars["Index"] = i
				n.RunChildren(nil)
			}

			n.SkipNode()

			return
		}

		errorType := "Error@FileScript/core.Parse[BAD_PARAMS@For]"
		common.PrintErrorAndExit(errorType, fmt.Sprintf("标签 @For 只能使用 int 类型参数"))
		return
	}

	if n.MatchCaller("Dir") {
		val := GetCtx(n.Callee, n.ContextNode)

		if dirPath, ok := val.(string); ok {

			list, err := dir.All(dirPath)

			if err != nil {
				errorType := "Error@FileScript/core.Parse[PATH_NOT_FOUND@Dir]"
				common.PrintErrorAndExit(errorType, fmt.Sprintf("路径[%v]读取失败", dirPath))

			}

			for i, p := range list {
				n.ContextNode.Vars["Index"] = i
				n.ContextNode.Vars["Path"] = p
				n.ContextNode.Vars["Dir"] = path.Dir(p)
				n.ContextNode.Vars["Ext"] = path.Ext(p)
				n.ContextNode.Vars["Name"] = path.Base(p)
				n.ContextNode.Vars["AbsPath"], _ = filepath.Abs(p)

				n.RunChildren(nil)
			}

			n.SkipNode()

			return
		}

		errorType := "Error@FileScript/core.Parse[BAD_PARAMS@Dir]"
		common.PrintErrorAndExit(errorType, fmt.Sprintf("标签 @Dir 参数不正确"))

		return
	}

	if n.MatchCaller("Rows") {
		val := GetCtx(n.Callee, n.ContextNode)

		if p, ok := val.(string); ok {

			lines, err := fs.ReadLines(p)

			if err != nil {
				errorType := "Error@FileScript/core.Parse[PATH_NOT_FOUND@Row]"
				common.PrintErrorAndExit(errorType, fmt.Sprintf("路径[%v]读取失败", p))

			}

			for i, line := range lines {
				n.ContextNode.Vars["Index"] = i
				n.ContextNode.Vars["Content"] = line

				n.RunChildren(nil)
			}

			n.SkipNode()
			return
		}

		errorType := "Error@FileScript/core.Parse[BAD_PARAMS@Row]"
		common.PrintErrorAndExit(errorType, fmt.Sprintf("标签 @Row 参数不正确"))
		return
	}

	if n.MatchCaller("Equal") {
		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@Equal]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@Equal 语句格式必须为(绑定的变量, 常量)"))
		}

		lhs := GetCtx(vars[0], n.ContextNode)
		rhs := GetCtx(vars[1], n.ContextNode)

		n.ContextNode.prev.Vars["True"] = lhs == rhs
		n.ContextNode.prev.Vars["False"] = lhs != rhs
		return
	}

	if n.MatchCaller("Compare") {
		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@Compare]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@Compare 语句格式必须为(变量, 常量)"))
		}

		flag := true
		lhs := GetCtx(vars[0], n.ContextNode)
		rhs := GetCtx(vars[1], n.ContextNode)

		switch a := lhs.(type) {
		case string:
			{
				if b, ok := rhs.(string); ok {
					flag = len(a) > len(b)
				}
			}
		case int:
			{
				if b, ok := rhs.(int); ok {
					flag = a > b
				}

				if b, ok := rhs.(float64); ok {
					flag = float64(a) > b
				}
			}
		case float64:
			{
				if b, ok := rhs.(int); ok {
					flag = a > float64(b)
				}

				if b, ok := rhs.(float64); ok {
					flag = a > b
				}
			}
		default:
			errorType := "Error@FileScript/core.Parse[BAD_PARAMS@Compare]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("标签 @Compare 参数不正确\n只支持[字符串与字符串]或[数值与数值]之间的比较"))
		}

		n.ContextNode.prev.Vars["True"] = flag
		n.ContextNode.prev.Vars["False"] = !flag
		return
	}

	if n.MatchCaller("Contain") {
		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@Compare]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@Compare 语句格式必须为(字符串, 字符串)"))
		}

		flag := true
		lhs := GetCtx(vars[0], n.ContextNode)
		rhs := GetCtx(vars[1], n.ContextNode)

		switch a := lhs.(type) {
		case string:
			{
				if b, ok := rhs.(string); ok {
					flag = strings.Contains(a, b)
				}
			}

		default:
			errorType := "Error@FileScript/core.Parse[BAD_PARAMS@Contain]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("标签 @Contain 参数不正确\n只支持(字符串, 字符串)形式的参数"))
		}

		n.ContextNode.prev.Vars["True"] = flag
		n.ContextNode.prev.Vars["False"] = !flag
		return
	}

	if n.MatchCaller("Write") {

		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@Write]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@Write 语句格式必须为(路径, 内容)"))
		}

		root := GetCtx(vars[0], n.ContextNode)
		content := GetCtx(vars[1], n.ContextNode)

		fs.New(fmt.Sprint(root)).Append(fmt.Sprint(content)).Check(func(err []error) {
			if len(err) > 0 {
				errorType := "Error@FileScript/core.Parse[RUNNING_ERR@Write]"
				common.PrintErrorAndExit(errorType, fmt.Sprintf("运行时错误:\n%v", err[0]))
			}
		})

		return
	}

	if n.MatchCaller("Rename") {

		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@Rename]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@Rename 语句格式必须为(路径, 文件名)"))
		}

		root := GetCtx(vars[0], n.ContextNode)
		content := GetCtx(vars[1], n.ContextNode)

		err := fs.Rename(fmt.Sprint(root), fmt.Sprint(content))

		if err != nil {
			errorType := "Error@FileScript/core.Parse[RUNNING_ERR@Rename]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("运行时错误:\n%v", err))
		}

		return
	}

	if n.MatchCaller("Add") {
		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@Add]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@Add 语句格式必须为(变量, 变量)"))
		}

		lhs := GetCtx(vars[0], n.ContextNode)
		rhs := GetCtx(vars[1], n.ContextNode)
		switch a := lhs.(type) {
		case int:
			{
				if b, ok := rhs.(int); ok {
					n.ContextNode.prev.Vars["Result"] = a + b
					return
				}

				if b, ok := rhs.(float64); ok {
					n.ContextNode.prev.Vars["Result"] = float64(a) + b
					return
				}

				if b, ok := rhs.(string); ok {
					n.ContextNode.prev.Vars["Result"] = fmt.Sprint(a, b)
					return
				}
			}
		case string:
			{
				if b, ok := rhs.(string); ok {
					n.ContextNode.prev.Vars["Result"] = a + b
					return
				}
			}
		default:
			errorType := "Error@FileScript/core.Parse[BAD_PARAMS@Add]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("标签 @Add 参数不正确"))
		}

		return
	}

	if n.MatchCaller("Sub") {
		vars := Get2Params(n.Callee)
		if vars == nil {
			errorType := "Error@FileScript/core.Parse[BAD_FORMAT@Sub]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("@Sub 语句格式必须为(变量, 变量)"))
		}

		lhs := GetCtx(vars[0], n.ContextNode)
		rhs := GetCtx(vars[1], n.ContextNode)

		switch a := lhs.(type) {
		case int:
			{
				if b, ok := rhs.(int); ok {
					n.ContextNode.prev.Vars["Result"] = a - b
					return
				}

				if b, ok := rhs.(float64); ok {
					n.ContextNode.prev.Vars["Result"] = float64(a) - b
					return
				}

				if b, ok := rhs.(string); ok {
					n.ContextNode.prev.Vars["Result"] = strings.Replace(fmt.Sprint(a), b, "", -1)
					return
				}
			}
		case string:
			{
				if b, ok := rhs.(string); ok {
					n.ContextNode.prev.Vars["Result"] = strings.Replace(a, b, "", -1)
					return
				}
			}
		default:
			errorType := "Error@FileScript/core.Parse[BAD_PARAMS@Sub]"
			common.PrintErrorAndExit(errorType, fmt.Sprintf("标签 @Sub 参数不正确"))
		}

		return
	}

	errorType := "Warn@FileScript/core.Parse[UNKNOWN_TAG]"
	common.PrintError(errorType, fmt.Sprintf("未知的语句 %v", n.Caller))

}
